home *** CD-ROM | disk | FTP | other *** search
- ;;; dsssl.el --- DSSSL parser
- ;; Author: wmperry
- ;; Created: 1997/10/17 14:07:55
- ;; Version: 1.16
- ;; Keywords:
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;; Copyright (c) 1996, 1997 by William M. Perry <wmperry@cs.indiana.edu>
- ;;; Copyright (c) 1997 by Free Software Foundation, Inc.
- ;;;
- ;;; This file is part of GNU Emacs.
- ;;;
- ;;; GNU Emacs is free software; you can redistribute it and/or modify
- ;;; it under the terms of the GNU General Public License as published by
- ;;; the Free Software Foundation; either version 2, or (at your option)
- ;;; any later version.
- ;;;
- ;;; GNU Emacs is distributed in the hope that it will be useful,
- ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
- ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ;;; GNU General Public License for more details.
- ;;;
- ;;; You should have received a copy of the GNU General Public License
- ;;; along with GNU Emacs; see the file COPYING. If not, write to the
- ;;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- ;;; Boston, MA 02111-1307, USA.
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
- (require 'cl)
- (require 'dsssl-flow)
-
- (if (not (fboundp 'cl-copy-hashtable))
- (defun cl-copy-hashtable (h)
- (let ((new (make-hash-table)))
- (cl-maphash (function (lambda (k v) (cl-puthash k v new))) h)
- new)))
-
- (defconst dsssl-builtin-functions
- '(not boolean\? case equal\? null\? list\? list length append
- reverse list-tail list-ref member symbol\? keyword\? quantity\?
- number\? real\? integer\? = < > <= >= + * - / max min abs quotient
- modulo remainder floor ceiling truncate round number->string
- string->number char\? char=\? char-property string\? string
- string-length string-ref string=\? substring string-append
- procedure\? apply external-procedure make time time->string quote
- char-downcase identity error let)
- "A list of all the builtin DSSSL functions that we support.")
-
- (defsubst dsssl-check-args (args expected)
- ;; Signal an error if we don't have the expected # of arguments
- (or (= (length args) expected)
- (error "Wrong # arguments (expected %d): %d" expected (length args))))
-
- (defsubst dsssl-min-args (args min)
- (or (>= (length args) min)
- (error "Wrong # arguments (expected at least %d): %d" min
- (length args))))
-
- (defun dsssl-call-function (func args)
- (declare (special defines units))
- (let ((old-defines nil)
- (old-units nil)
- (func-args (nth 1 func))
- (real-func (nth 2 func))
- (retval nil))
- ;; Make sure we got the right # of arguments
- (dsssl-check-args args (length func-args))
-
- ;; make sure we evaluate all the arguments in the old environment
- (setq args (mapcar 'dsssl-eval args))
-
- ;; Save the old environment
- (setq old-defines (cl-copy-hashtable defines)
- old-units (cl-copy-hashtable units))
-
- ;; Create the function's environment
- (while func-args
- (cl-puthash (car func-args) (car args) defines)
- (setq func-args (cdr func-args)
- args (cdr args)))
-
- ;; Now evaluate the function body, returning the value of the last one
- (while real-func
- (setq retval (dsssl-eval (car real-func))
- real-func (cdr real-func)))
-
- ;; Restore the previous environment
- (setq defines old-defines
- units old-units)
-
- ;; And we are out of here baby!
- retval))
-
- (defun dsssl-eval (form)
- ;; We expect to have a 'defines' and 'units' hashtable floating around
- ;; from higher up the call stack.
- (declare (special defines units))
- (cond
- ((consp form) ; A function call
- (let ((func (car form))
- (args (cdr form)))
- (case func
- (cons
- (dsssl-check-args args 2)
- (cons (dsssl-eval (pop args)) (dsssl-eval (pop args))))
- (cdr
- (dsssl-check-args args 1)
- (cdr (dsssl-eval (pop args))))
- (car
- (dsssl-check-args args 1)
- (car (dsssl-eval (pop args))))
- (not
- (dsssl-check-args args 1)
- (not (dsssl-eval (car args))))
- (boolean\?
- (dsssl-check-args args 1)
- (and (symbolp (car args))
- (memq (car args) '(\#f \#t))))
- (if
- (dsssl-min-args args 2)
- (let ((val (dsssl-eval (pop args))))
- (if val
- (dsssl-eval (nth 0 args))
- (if (nth 1 args)
- (dsssl-eval (nth 1 args))))))
- (let ; FIXME
- )
- (case
- (dsssl-min-args args 2)
- (let* ((val (dsssl-eval (pop args)))
- (conditions args)
- (done nil)
- (possibles nil)
- (cur nil))
- (while (and conditions (not done))
- (setq cur (pop conditions)
- possibles (nth 0 cur))
- (if (or (and (listp possibles)
- (member val possibles))
- (equal val possibles)
- (memq possibles '(default otherwise)))
- (setq done (dsssl-eval (nth 1 cur)))))
- done))
- (equal\?
- (dsssl-check-args args 2)
- (equal (dsssl-eval (car args)) (dsssl-eval (cadr args))))
- (null\?
- (dsssl-check-args args 1)
- (null (dsssl-eval (car args))))
- (list\?
- (dsssl-check-args args 1)
- (listp (dsssl-eval (car args))))
- (list
- (mapcar 'dsssl-eval args))
- (length
- (dsssl-check-args args 1)
- (length (dsssl-eval (car args))))
- (append
- (apply 'append (mapcar 'dsssl-eval args)))
- (reverse
- (dsssl-check-args args 1)
- (reverse (dsssl-eval (car args))))
- (list-tail
- (dsssl-check-args args 2)
- (nthcdr (dsssl-eval (car args)) (dsssl-eval (cadr args))))
- (list-ref
- (dsssl-check-args args 2)
- (nth (dsssl-eval (car args)) (dsssl-eval (cadr args))))
- (member
- (dsssl-check-args args 2)
- (member (dsssl-eval (car args)) (dsssl-eval (cadr args))))
- (symbol\?
- (dsssl-check-args args 1)
- (symbolp (dsssl-eval (car args))))
- (keyword\?
- (dsssl-check-args args 1)
- (keywordp (dsssl-eval (car args))))
- (quantity\?
- (dsssl-check-args args 1)
- (error "%s not implemented yet." func))
- (number\?
- (dsssl-check-args args 1)
- (numberp (dsssl-eval (car args))))
- (real\?
- (dsssl-check-args args 1)
- (let ((rval (dsssl-eval (car args))))
- (and (numberp rval)
- (/= (truncate rval) rval))))
- (integer\?
- (dsssl-check-args args 1)
- (let ((rval (dsssl-eval (car args))))
- (and (numberp rval)
- (= (truncate rval) rval))))
- ((= < > <= >=)
- (dsssl-min-args args 2)
- (let ((not-done t)
- (initial (dsssl-eval (car args)))
- (next nil))
- (setq args (cdr args))
- (while (and args not-done)
- (setq next (dsssl-eval (car args))
- args (cdr args)
- not-done (funcall func initial next)
- initial next))
- not-done))
- ((+ *)
- (dsssl-min-args args 2)
- (let ((acc (dsssl-eval (car args))))
- (setq args (cdr args))
- (while args
- (setq acc (funcall func acc (dsssl-eval (car args)))
- args (cdr args)))
- acc))
- (-
- (dsssl-min-args args 1)
- (apply func (mapcar 'dsssl-eval args)))
- (/
- (dsssl-min-args args 1)
- (if (= (length args) 1)
- (/ 1 (dsssl-eval (car args)))
- (apply func (mapcar 'dsssl-eval args))))
- ((max min)
- (apply func (mapcar 'dsssl-eval args)))
- (abs
- (dsssl-check-args args 1)
- (abs (dsssl-eval (car args))))
- (quotient ; FIXME
- (error "`%s' not implemented yet!" func))
- (modulo
- (dsssl-check-args args 2)
- (mod (dsssl-eval (car args)) (dsssl-eval (cadr args))))
- (remainder
- (dsssl-check-args args 2)
- (% (dsssl-eval (car args)) (dsssl-eval (cadr args))))
- ((floor ceiling truncate round)
- (dsssl-check-args args 1)
- (funcall func (dsssl-eval (car args))))
- (number->string
- (dsssl-min-args args 1)
- (if (= (length args) 1)
- (number-to-string (dsssl-eval (car args)))
- (if (= (length args) 2) ; They gave us a radix
- (error "Radix arg not supported yet.")
- (dsssl-check-args args 1))))
- (string->number
- (dsssl-min-args args 1)
- (if (= (length args) 1)
- (string-to-number (dsssl-eval (car args)))
- (if (= (length args) 2) ; They gave us a radix
- (error "Radix arg not supported yet.")
- (dsssl-check-args args 1))))
- (char\?
- (dsssl-check-args args 1)
- (characterp (dsssl-eval (car args))))
- (char=\?
- (dsssl-check-args args 2)
- (char-equal (dsssl-eval (car args)) (dsssl-eval (cadr args))))
- (char-downcase
- (dsssl-check-args args 1)
- (downcase (dsssl-eval (car args))))
- (char-property ; FIXME
- (error "`%s' not implemented yet!" func))
- (string\?
- (dsssl-check-args args 1)
- (stringp (dsssl-eval (car args))))
- (string
- (dsssl-min-args args 1)
- (mapconcat 'char-to-string (mapcar 'dsssl-eval args) ""))
- (string-length
- (dsssl-check-args args 1)
- (length (dsssl-eval (car args))))
- (string-ref
- (dsssl-check-args args 2)
- (aref (dsssl-eval (car args)) (dsssl-eval (cadr args))))
- (string=\?
- (dsssl-check-args args 2)
- (string= (dsssl-eval (car args)) (dsssl-eval (cadr args))))
- (substring
- (substring (dsssl-eval (pop args))
- (dsssl-eval (pop args))
- (dsssl-eval (pop args))))
- (string-append
- (let ((rval ""))
- (while args
- (setq rval (concat rval (dsssl-eval (pop args)))))
- rval))
- (procedure\?
- (dsssl-check-args args 1)
- (let* ((sym (dsssl-eval (car args)))
- (def (cl-gethash sym defines)))
- (or (memq sym dsssl-builtin-functions)
- (and def (listp def) (eq (car def) 'lambda)))))
- (apply ; FIXME
- )
- (external-procedure ; FIXME
- )
- (make
- (let* ((type (dsssl-eval (pop args)))
- (symname nil)
- (props nil)
- (tail nil)
- (children nil)
- (temp nil)
- )
- ;; Massage :children into the last slot
- (setq props (mapcar 'dsssl-eval args)
- tail (last props)
- children (car tail))
- (if (consp tail)
- (setcar tail nil))
- (if (not (car props))
- (setq props nil))
- (setq temp (- (length props) 1))
- ;; Not sure if we should really bother with this or not, but
- ;; it does at least make it look more common-lispy keywordish
- ;; and such. DSSSL keywords look like font-weight:, this makes
- ;; it :font-weight
- (while (>= temp 0)
- (setq symname (symbol-name (nth temp props)))
- (if (string-match "^\\(.*\\):$" symname)
- (setf (nth temp props)
- (intern (concat ":" (match-string 1 symname)))))
- (setq temp (- temp 2)))
-
- ;; Create the actual flow object
- (make-flow-object :type type
- :children children
- :properties props)
- )
- )
- (time
- (mapconcat 'int-to-string (current-time) ":"))
- (time->string
- (dsssl-check-args args 1)
- (current-time-string
- (mapcar 'string-to-int
- (split-string (dsssl-eval (car args)) ":"))))
- (quote
- (dsssl-check-args args 1)
- (car args))
- (identity
- (dsssl-check-args args 1)
- (dsssl-eval (car args)))
- (error
- (apply 'error (mapcar 'dsssl-eval args)))
- (otherwise
- ;; A non-built-in function - look it up
- (let ((def (cl-gethash func defines)))
- (if (and def (listp def) (eq (car def) 'lambda))
- (dsssl-call-function def args)
- (error "Symbol's function definition is void: %s" func))))
- )
- )
- )
- ((symbolp form) ; A variable
- ;; A DSSSL keyword!
- (if (string-match ":$" (symbol-name form))
- form
- (let ((val (cl-gethash form defines 'ThIS-Is_A_BOgUs-VariuhhBBLE)))
- (if (not (eq val 'ThIS-Is_A_BOgUs-VariuhhBBLE))
- val
- ;; Ok, we got a bogus variable, but maybe it is really a UNIT
- ;; dereference. Check.
- (let ((name (symbol-name form))
- (the-units nil)
- (number nil)
- (conversion nil))
- (if (not (string-match "^\\([0-9.]+\\)\\([a-zA-Z]+\\)$" name))
- (error "Symbol's value as variable is void: %s" form)
- (setq number (string-to-int (match-string 1 name))
- the-units (intern (downcase (match-string 2 name)))
- conversion (cl-gethash the-units units))
- (if (or (not conversion) (not (numberp conversion)))
- (error "Symbol's value as variable is void: %s" form)
- (* number conversion))))))))
- (t
- form)
- )
- )
-
- (defsubst dsssl-predeclared ()
- (declare (special defines units))
- (cl-puthash '\#f nil defines)
- (cl-puthash 'nil nil defines)
- (cl-puthash '\#t t defines)
- ;; NOTE: All units are stored internally as points.
- (cl-puthash 'in (float 72) units)
- (cl-puthash 'mm (float (* 72 25.4)) units)
- (cl-puthash 'cm (float (* 72 2.54)) units)
- )
-
- (defun dsssl-parse (buf)
- ;; Return the full representation of the DSSSL stylesheet as a series
- ;; of LISP objects.
- (let ((defines (make-hash-table :size 13))
- (units (make-hash-table :size 13))
- (buf-contents nil))
- (dsssl-predeclared)
- (save-excursion
- (setq buf-contents (if (or (bufferp buf) (get-buffer buf))
- (progn
- (set-buffer buf)
- (buffer-string))
- buf))
- (set-buffer (generate-new-buffer " *dsssl-style*"))
- (insert buf-contents)
- (goto-char (point-min))
- (skip-chars-forward " \t\n\r")
- (if (looking-at "<!") ; DOCTYPE present
- (progn
- ;; This should _DEFINITELY_ be smarter
- (search-forward ">" nil t)
- ))
- (let ((result nil)
- (temp nil)
- (save-pos nil))
- (while (not (eobp))
- (condition-case ()
- (setq save-pos (point)
- temp (read (current-buffer)))
- (invalid-read-syntax
- ;; This disgusting hack is in here so that we can basically
- ;; extend the lisp reader to gracefully deal with converting
- ;; DSSSL #\A to Emacs-Lisp ?A notation. If you know of a
- ;; better way, please feel free to send me some email.
- (setq temp nil)
- (backward-char 1)
- (if (looking-at "#\\\\")
- (replace-match "?")
- (insert "\\"))
- (goto-char save-pos))
- (error nil))
- (cond
- ((null temp)
- nil)
- ((listp temp)
- (case (car temp)
- (define-unit
- (cl-puthash (cadr temp) (dsssl-eval (caddr temp))
- units))
- (define
- (if (listp (cadr temp))
- ;; A function
- (cl-puthash (caadr temp)
- (list 'lambda
- (cdadr temp)
- (cddr temp)) defines)
- ;; A normal define
- (cl-puthash (cadr temp)
- (dsssl-eval (caddr temp)) defines)))
- (otherwise
- (setq result (cons temp result)))))
- (t
- (setq result (cons temp result))))
- (skip-chars-forward " \t\n\r"))
- (kill-buffer (current-buffer))
- (list defines units (nreverse result))))))
-
- (defun dsssl-test (x)
- (let* ((result (dsssl-parse x))
- (defines (nth 0 result))
- (units (nth 1 result))
- (forms (nth 2 result)))
- (mapcar 'dsssl-eval forms)))
-
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;; The flow object classes.
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- (defmacro flow-object-property (obj prop &optional default)
- "Return property PROP of the DSSSL flow object OBJ.
- OBJ can be any flow object class, as long as it was properly derived
- from the base `flow-object' class."
- (` (plist-get (flow-object-properties (, obj)) (, prop) (, default))))
-
- ;; Now for specific types of flow objects
- ;; Still to do:
- ;;; display-group
- ;;; paragraph
- ;;; sequence
- ;;; line-field
- ;;; paragraph-break
- ;;; simple-page-sequence
- ;;; score
- ;;; table
- ;;; table-row
- ;;; table-cell
- ;;; rule
- ;;; external-graphic
-
-
- (provide 'dsssl)
-